using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using Microsoft.ApplicationBlocks.Data;
using System.Xml;

namespace Microsoft.SharedSource.SqlServer.MDDE.Repository
{
    public sealed class InstanceColumnCollection : IEnumerable
    {
        #region Fields

        private Dictionary<string, InstanceColumn>
            instanceColumns =
            new Dictionary<string, InstanceColumn>();
        InstanceConfigurableComponent parentComponent;
        InstanceColumnCollectionProperties properties;
        int id;
        string collectionType;
        string name;

        #endregion

        #region Properties

        public InstanceColumnCollection InstanceColumns
        {
            get
            {
                if (instanceColumns == null)
                    instanceColumns =
                        new Dictionary<string, InstanceColumn>();
                return this;
            }
        }

        public InstanceColumnCollectionProperties Properties
        {
            get
            {
                if (properties == null)
                    properties =
                        new InstanceColumnCollectionProperties(this);
                return properties;
            }
        }

        public InstanceConfigurableComponent ParentComponent
        {
            get { return parentComponent; }
        }

        public int Id
        {
            get { return id; }
        }

        public string CollectionType
        {
            get { return collectionType; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public int Count
        {
            get { return instanceColumns.Count; }
        }

        public InstanceColumn this[string columnName]
        {
            get { return instanceColumns[columnName]; }
        }

        #endregion

        #region Methods
        public InstanceColumnCollection(
            InstanceConfigurableComponent parentComponent,
            string collectionType, string collectionName)
            : this(parentComponent, collectionType, collectionName, null) { }

        public InstanceColumnCollection(
            InstanceConfigurableComponent parentComponent,
            string collectionType, string collectionName, SqlTransaction txn)
        {
            this.parentComponent = parentComponent;
            this.collectionType = collectionType;
            this.name = collectionName;
            this.properties =
                new InstanceColumnCollectionProperties(this);
            this.instanceColumns =
                new Dictionary<string, InstanceColumn>();

            // See if we can get this collection from repository.
            SqlParameter[] sqlParams = new SqlParameter[3];
            sqlParams[0] = new SqlParameter("componentId",
                parentComponent.Id);
            sqlParams[1] = new SqlParameter("collectionType",
                this.CollectionType);
            sqlParams[2] = new SqlParameter("collectionName",
                this.Name);

            DataSet columnCollectionInfo;
            if (txn == null)
                columnCollectionInfo =
                SqlHelper.ExecuteDataset(
                parentComponent.ParentInstance.ParentTemplate.
                ParentRepository.RepositoryConnection,
                CommandType.Text,
                Resource.GetInstanceCollectionInfo, sqlParams);
            else
                columnCollectionInfo =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetInstanceCollectionInfo, sqlParams);

            // Return if we find nothing.
            if (columnCollectionInfo.Tables[0].Rows.Count == 0)
                return;

            // Otherwise, set the collection info.
            DataRow r = columnCollectionInfo.Tables[0].Rows[0];
            this.id =
                Convert.ToInt32(r["CollectionId"]);

            // See if we have any collection properties.
            DataSet collectionPropertyNames;
            if (txn == null)
                collectionPropertyNames =
                SqlHelper.ExecuteDataset(
                parentComponent.ParentInstance.ParentTemplate.
                ParentRepository.RepositoryConnection,
                CommandType.Text,
                Resource.GetCollectionPropertyNames,
                new SqlParameter("collectionId", this.id));
            else
                collectionPropertyNames =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetCollectionPropertyNames,
                new SqlParameter("collectionId", this.id));

            // Add in each of the above properties to internal list.
            foreach (DataRow propName in
                collectionPropertyNames.Tables[0].Rows)
            {
                // Create a new property object.
                InstanceColumnCollectionProperty prop =
                    new InstanceColumnCollectionProperty(
                    this, propName["PropertyName"].ToString(), txn);

                // Add this property to list.
                this.properties.Add(prop);
            }

            // See if we have any columns for this collection.

            DataSet instanceColumnNames;
            if (txn == null)
                instanceColumnNames =
                SqlHelper.ExecuteDataset(
                parentComponent.ParentInstance.ParentTemplate.
                ParentRepository.RepositoryConnection,
                CommandType.Text,
                Resource.GetInstanceColumnNames,
                new SqlParameter("collectionId", this.id));
            else
                instanceColumnNames =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetInstanceColumnNames,
                new SqlParameter("collectionId", this.id));

            // Add in each of the above properties to internal list.
            foreach (DataRow rInstCol in
                instanceColumnNames.Tables[0].Rows)
            {
                // Create a new 'InstanceColumn' object.
                InstanceColumn objInstCol =
                    new InstanceColumn(
                    this, rInstCol["ColumnName"].ToString(), txn);

                // Add this instance column to list.
                this.instanceColumns.Add(objInstCol.Name, objInstCol);
            }
        }

        public void Add(InstanceColumn instanceColumn)
        {
            instanceColumns.Add(
                instanceColumn.Name, instanceColumn);
        }

        public void Remove(InstanceColumn instanceColumn)
        {
            instanceColumns.Remove(instanceColumn.Name);
        }

        public void Clear()
        {
            instanceColumns.Clear();
        }

        public bool ContainsInstanceColumn(string columnName)
        {
            return instanceColumns.ContainsKey(columnName);
        }

        public void Create(SqlTransaction inputTxn)
        {
            // Create a new transaction if there is no input transaction.
            SqlTransaction txn = inputTxn != null ? inputTxn :
                parentComponent.ParentInstance.ParentTemplate.
                ParentRepository.
                RepositoryConnection.BeginTransaction();

            try
            {
                SqlParameter[] sqlParams = new SqlParameter[3];
                sqlParams[0] =
                    new SqlParameter("componentId",
                    parentComponent.Id);
                sqlParams[1] =
                    new SqlParameter("collectionType",
                    this.CollectionType);
                sqlParams[2] =
                    new SqlParameter("collectionName",
                    this.Name);

                this.id =
                    Convert.ToInt32(
                    SqlHelper.ExecuteScalar(txn,
                    CommandType.StoredProcedure,
                    "sp_CreateInstanceColumnCollection",
                    sqlParams));

                // Create the collection properties.
                foreach (InstanceColumnCollectionProperty prop
                    in this.Properties)
                    prop.Create(txn);

                // Create the collection columns
                foreach (InstanceColumn col in this.InstanceColumns)
                    col.Create(txn);

                // Commit the transaction if it is not an input one.
                if (inputTxn == null)
                    txn.Commit();
            }
            catch (Exception ex)
            {
                // Rollback the transaction if it is not an input one.
                if (inputTxn == null)
                    txn.Rollback();
                throw ex;
            }
        }

        internal void Update(SqlTransaction txn)
        {
            #region Update the collection properties.

            DataSet collPropertyInfo =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetCollectionPropertyNames,
                new SqlParameter("@collectionId", this.Id));

            List<string> toBeDeleted =
                new List<string>();

            List<InstanceColumnCollectionProperty> toBeUpdated =
                new List<InstanceColumnCollectionProperty>();

            List<InstanceColumnCollectionProperty> toBeInserted =
                new List<InstanceColumnCollectionProperty>();

            Hashtable repositoryCollectionProperties = new Hashtable();
            foreach (DataRow r0 in collPropertyInfo.Tables[0].Rows)
            {
                repositoryCollectionProperties.Add(r0["PropertyName"], null);

                // Find all the properties that 
                // are not present in the collection 
                // and delete them from the repository.
                if (!this.Properties.ContainsProperty(
                    r0["PropertyName"].ToString()))
                    toBeDeleted.Add(r0["PropertyName"].ToString());
            }

            foreach (InstanceColumnCollectionProperty collectionProperty
                in this.Properties)
            {
                // Find all the properties that 
                // are already present and update 
                // them in the repository.
                if (repositoryCollectionProperties.
                    Contains(collectionProperty.Name))
                    toBeUpdated.Add(collectionProperty);
                // Find new members and insert 
                // them into the repository.
                else
                    toBeInserted.Add(collectionProperty);
            }

            // Delete the properties.
            foreach (string propToBeDeleted in toBeDeleted)
            {
                InstanceColumnCollectionProperty prop =
                    new InstanceColumnCollectionProperty(
                    this, propToBeDeleted, txn);

                prop.Delete(txn);
            }

            // Update the properties.
            foreach (InstanceColumnCollectionProperty prop in toBeUpdated)
                prop.Update(txn);

            // Insert new properties.
            foreach (InstanceColumnCollectionProperty prop in toBeInserted)
                prop.Create(txn);

            #endregion

            #region Update the collection columns.

            DataSet colInfo =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetInstanceColumnNames,
                new SqlParameter("@collectionId", this.Id));

            List<string> colToBeDeleted =
                new List<string>();

            List<InstanceColumn> colToBeUpdated =
                new List<InstanceColumn>();

            List<InstanceColumn> colToBeInserted =
                new List<InstanceColumn>();

            Hashtable repositoryColumns = new Hashtable();
            foreach (DataRow r0 in colInfo.Tables[0].Rows)
            {
                repositoryColumns.Add(r0["ColumnName"], null);

                // Find all the columns that 
                // are not present in the collection 
                // and delete them from the repository.
                if (!this.InstanceColumns.ContainsInstanceColumn(
                    r0["ColumnName"].ToString()))
                    colToBeDeleted.Add(r0["ColumnName"].ToString());
            }

            foreach (InstanceColumn instanceColumn
                in this.InstanceColumns)
            {
                // Find all the columns that 
                // are already present and update 
                // them in the repository.
                if (repositoryColumns.
                    Contains(instanceColumn.Name))
                    colToBeUpdated.Add(instanceColumn);
                // Find new members and insert 
                // them into the repository.
                else
                    colToBeInserted.Add(instanceColumn);
            }

            // Delete the columns.
            foreach (string col in colToBeDeleted)
            {
                InstanceColumn instanceCol =
                    new InstanceColumn(this, col, txn);

                instanceCol.Delete(txn);
            }

            // Update the properties.
            foreach (InstanceColumn col in colToBeUpdated)
                col.Update(txn);

            // Insert new properties.
            foreach (InstanceColumn col in colToBeInserted)
                col.Create(txn);

            #endregion
        }

        internal void Delete(SqlTransaction txn)
        {
            // Delete collection.
            SqlHelper.ExecuteNonQuery(txn,
                CommandType.StoredProcedure,
                "sp_DeleteInstanceColumnCollection",
                new SqlParameter(
                "collectionId", this.Id));
        }
        #endregion

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return instanceColumns.Values.GetEnumerator();
        }

        #endregion

        internal void ApplyProperties(XmlNode xCollection, bool createAfresh)
        {
            // Remove all the scalar properties.
            if (createAfresh)
                this.properties.Clear();

            // Add scalar properties.
            foreach (XmlNode xProperty in xCollection.SelectNodes("./Properties/Property"))
            {
                string propertyName =
                    xProperty.Attributes["Name"].Value;

                // Create new scalar property.
                InstanceColumnCollectionProperty prop;
                if (this.Properties.ContainsProperty(propertyName))
                    prop = this.Properties[propertyName];
                else
                    prop = new InstanceColumnCollectionProperty(this, propertyName);

                // Apply changes to scalar property.
                prop.ApplyProperties(xProperty);

                // Add it to collection.
                if (!this.Properties.ContainsProperty(propertyName))
                    this.properties.Add(prop);
            }

            // Remove the scalar properties not found in the document.
            List<InstanceColumnCollectionProperty> propToBeRemoved =
                new List<InstanceColumnCollectionProperty>();
            foreach (InstanceColumnCollectionProperty property in this.Properties)
                if (xCollection.SelectSingleNode(
                    "./Properties/Property[@Name=\"" + property.Name + "\"]") == null)
                    propToBeRemoved.Add(property);
            foreach (InstanceColumnCollectionProperty property in propToBeRemoved)
                this.Properties.Remove(property);

            // Remove all the columns.
            if (createAfresh)
                this.instanceColumns.Clear();

            // Add columns.
            foreach (XmlNode xColumn in xCollection.SelectNodes("./Columns/Column"))
            {
                string columnName =
                    xColumn.Attributes["Name"].Value;

                // Create new column.
                InstanceColumn column;
                if (this.InstanceColumns.ContainsInstanceColumn(columnName))
                    column = this.InstanceColumns[columnName];
                else
                    column = new InstanceColumn(this, columnName);

                // Apply changes to mapping.
                column.ApplyProperties(xColumn, createAfresh);

                // Add it to collection.
                if (!this.InstanceColumns.ContainsInstanceColumn(columnName))
                    this.instanceColumns.Add(columnName, column);
            }

            // Remove the columns not found in the document.
            List<InstanceColumn> columnsToBeRemoved = new List<InstanceColumn>();
            foreach (InstanceColumn column in this.InstanceColumns)
                if (xCollection.SelectSingleNode(
                    "./Columns/Column[@Name=\"" + column.Name + "\"]") == null)
                    columnsToBeRemoved.Add(column);
            foreach (InstanceColumn column in columnsToBeRemoved)
                this.InstanceColumns.Remove(column);
        }
    }

    public sealed class ComponentColumnCollections : IEnumerable
    {
        #region Fields

        private Dictionary<string, InstanceColumnCollection>
            columnCollections =
            new Dictionary<string, InstanceColumnCollection>();
        InstanceConfigurableComponent parentComponent;

        #endregion

        #region Properties

        public int Count
        {
            get { return columnCollections.Count; }
        }

        public InstanceConfigurableComponent ParentComponent
        {
            get { return parentComponent; }
        }

        public InstanceColumnCollection this[string collectionType, string collectionName]
        {
            get
            {
                return columnCollections
              [collectionType + ";" + collectionName];
            }
        }

        #endregion

        #region Methods

        internal ComponentColumnCollections(
            InstanceConfigurableComponent parentComponent)
        {
            this.parentComponent = parentComponent;
        }

        public void Add(InstanceColumnCollection columnCollection)
        {
            columnCollections.Add(
                columnCollection.CollectionType +
                ";" + columnCollection.Name,
                columnCollection);
        }

        public void Remove(InstanceColumnCollection columnCollection)
        {
            columnCollections.Remove(
                columnCollection.CollectionType +
                ";" + columnCollection.Name);
        }

        public void Clear()
        {
            columnCollections.Clear();
        }

        public bool ContainsCollection(
            string collectionType, string collectionName)
        {
            return columnCollections.ContainsKey(
                collectionType + ";" + collectionName);
        }

        #endregion

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return columnCollections.Values.GetEnumerator();
        }

        #endregion
    }
}
